package com.easpeed.elevator.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.os.Process;
import android.util.Log;
import com.bigyu.utils.Logger;

import com.easpeed.comon.consts.CustomSetting;
import com.easpeed.elevator.R;
import com.easpeed.elevator.event.SendOrderEvent;
import com.google.gson.Gson;

import org.greenrobot.eventbus.EventBus;

import ai.onnxruntime.OnnxTensor;
import ai.onnxruntime.OrtEnvironment;
import ai.onnxruntime.OrtException;
import ai.onnxruntime.OrtSession;

public class FirDataProcessor {
    private static final String TAG = "FirDataProcessor";
    private static final int FRAME_SIZE = 250; // 每帧数据组数

//    private ExecutorService executorService;

    private OrtEnvironment env;
    private Map<String,OrtSession> session;
    private OrtSession currentSession;
    private String currentSessionName;

    int buffsize = 3;
    private final ArrayBlockingQueue<FloatBuffer> inputQueue = new ArrayBlockingQueue<>(buffsize - 2);
    private final ArrayBlockingQueue<OnnxTensor> tensorQueue = new ArrayBlockingQueue<>(buffsize - 2);
    final FloatBuffer[] inputBuffers = new FloatBuffer[buffsize];
    int currentBufferIndex = 0;

    // 构造函数
    public FirDataProcessor(Context context) {
        try {
            env = OrtEnvironment.getEnvironment();
            OrtSession.SessionOptions sessionOptions = new OrtSession.SessionOptions();
            sessionOptions.setIntraOpNumThreads(2); // 设置线程数
            session = new  HashMap<String,OrtSession>();
            if (CustomSetting.getUseDvsModel60()) {
                session.put("0", env.createSession(loadModel(context,R.raw.dvs_model_60_0), sessionOptions));
                session.put("80", env.createSession(loadModel(context,R.raw.dvs_model_60_80), sessionOptions));
                session.put("100", env.createSession(loadModel(context,R.raw.dvs_model_60_100), sessionOptions));
                session.put("120", env.createSession(loadModel(context,R.raw.dvs_model_60_120), sessionOptions));
                session.put("150", env.createSession(loadModel(context,R.raw.dvs_model_60_150), sessionOptions));
            } else {
                session.put("0", env.createSession(loadModel(context,R.raw.dvs_model_no), sessionOptions));
                session.put("80", env.createSession(loadModel(context,R.raw.dvs_model_80), sessionOptions));
                session.put("100", env.createSession(loadModel(context,R.raw.dvs_model_100), sessionOptions));
                session.put("120", env.createSession(loadModel(context,R.raw.dvs_model_120), sessionOptions));
                session.put("150", env.createSession(loadModel(context,R.raw.dvs_model_150), sessionOptions));
            }
            currentSession = session.get("100");
            currentSessionName = "100";
        } catch (OrtException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        startPipeline();


        for (int i = 0; i < buffsize; i++) {
            inputBuffers[i] = FloatBuffer.allocate(FRAME_SIZE * CustomSetting.getDvsPointNum());
        }
        startLoggingTimer();
    }

    private byte[] loadModel(Context context,int id) throws IOException {
        try (InputStream is = context.getResources().openRawResource(id);
             ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
            byte[] data = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(data)) != -1) {
                buffer.write(data, 0, bytesRead);
            }
            return buffer.toByteArray();
        }
    }

    public void setModel(String model){
        OrtSession cs = session.get(model);
        if (cs!=null){
            Logger.e(TAG, "设置滤波器模型：setModel    "+model);
            currentSession = cs;
            currentSessionName = model;
        }
    }

    public String getModel(){
        return currentSessionName ;
    }

    private void startPipeline() {
//        executorService = Executors.newFixedThreadPool(4);
        Thread t1 = new Thread(this::createTensor);
        t1.start();
        Thread t2 = new Thread(this::runInference);
        t2.start();
    }

    // 处理数据，输入为 int[]
    public void processData(float[] data) {
        FloatBuffer inputBuffer = inputBuffers[currentBufferIndex % buffsize];
        //如果是满的，就是旧数据，就扔掉
        if (!inputBuffer.hasRemaining()){
            inputBuffer.clear();
        }

//        Logger.d("ModelOutput", "input Data: " + inputData[0].length + "," + Arrays.toString(inputData[0]));

        inputBuffer.put(data);
        if (inputBuffer.hasRemaining()) {
            return;
        }

        if (!(inputQueue.remainingCapacity() > 0)) {
            Logger.e("FirFilters", "out one!");
            //搞不过来就丢包
            inputBuffer.clear();
            return;
        }
        currentBufferIndex++;
        inputBuffer.flip(); // 切换为读取模式
        try {
            inputQueue.put(inputBuffer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private void createTensor() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
        while (true) {
            try {
                FloatBuffer inputData = inputQueue.take();
//                x++;
//                if (x%8!=0) {
//                    continue;
//                }
                long startTime = System.currentTimeMillis();
                long[] inputNodeDims = new long[]{FRAME_SIZE, CustomSetting.getDvsPointNum()};
               
                OnnxTensor ont = OnnxTensor.createTensor(env, inputData, inputNodeDims);
                tensorQueue.put(ont);
                long endTime = System.currentTimeMillis();
                Logger.d("FirFilters", "Tensor creation time: " + (endTime - startTime) + " ms");
            } catch (InterruptedException | OrtException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void runInference() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
        while (true) {
            try {
                OnnxTensor ont = tensorQueue.take();
                long startTime = System.currentTimeMillis();
                Map<String, OnnxTensor> inputs = new HashMap<>();
                inputs.put("input", ont);
                OrtSession.Result result = currentSession.run(inputs);
                float[] outputData = (float[]) result.get(0).getValue();
//                Logger.d("FirFilters", "张量解析数据，outputData.length="+outputData.length);
                EventBus.getDefault().post(new SendOrderEvent(outputData, 0.4f,30)); // 发送差分后的数据
                logCount++;
                long endTime = System.currentTimeMillis();
                Logger.d("FirFilters", "Inference time: " + (endTime - startTime) + " ms");
//                Logger.d("ModelOutput", "Output Data: " + outputData.length + "," + Arrays.toString(outputData));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (OrtException e) {
                Thread.currentThread().interrupt();
                Logger.e("ModelOutput", e.toString());
            }
        }
    }


    int logCount = 0; // 计数器
    Timer timer = new Timer(); // 定时器

    // 启动计时器，统计下一秒内的日志数量
    public void startLoggingTimer() {
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Logger.d("FirDataProcessor4", "post event " + logCount);
                logCount = 0; // 重置计数
            }
        }, 0, 1000); // 每秒打印一次
    }

    public void close() throws OrtException {
//        session.close();
        env.close();
    }
}
